/**
 * @class 自定义层级细节
 * @author DuskyHuang 圣子
 * @description 自定义LOD。
*/

import { calcSphereScreenPercent } from 'algorithms';
import { _decorator, Component, Node, Camera, Vec3, } from 'cc';
import { View } from 'notifications';
import { core, Notifications, } from 'cfw';
import { solar } from '../../../scripts/proxies/solar-proxy';
const { ccclass } = _decorator;

@ccclass('cocosmos.CustomizedLOD')
export abstract class CustomizedLOD extends Component {

    protected abstract readonly LODValves: num.pct[];
    protected abstract init(): void;
    protected abstract onLod0Initiated(): void;
    protected abstract onTick(dt?: time.sec, tick?: num.int): void;
    declare protected cat: into;

    protected onLoad(): void {
        const rotator = this.node.getChildByName('Rotator');
        this._lod0 = this.node.getChildByName('LOD0') ?? rotator?.getChildByName('LOD0') ?? null;
        this._lod1 = this.node.getChildByName('LOD1') ?? rotator?.getChildByName('LOD1') ?? null;
        this._lod2 = this.node.getChildByName('LOD2') ?? null;
        this._lod3 = this.node.getChildByName('LOD3') ?? null;
        this._lods = [this._lod0, this._lod1, this._lod2, this._lod3];
        this._lods.clean();
        this._lods.forEach(lod => lod.active = false);
        core.on(Notifications.Time.TICK, this._tick, this);
        core.on(Notifications.Time.TICK, this.syncLODChoosing, this, -99);  // 最后更新否则上级系统稍有变化此处屏占比计算将有较大误差
        core.on(View.Scale.RATIO_CHANGE, this.updateScale, this);
        this.init();
    }

    protected onDestroy(): void {
        core.unload(this);
    }

    protected onEnable(): void {
        this.updateScale();
    }

    private _tick(dt?: time.sec, tick?: num.int): void {
        this.onTick(dt, tick);
    }

    protected syncLODChoosing(): void {
        if (!this.node.activeInHierarchy) return;
        const pct = calcSphereScreenPercent(this.node, this._viewer);
        let chosen: Node = null;
        for (let idx = 0; idx < this.LODValves.length; idx++) {
            const valve = this.LODValves[idx];
            if (pct >= valve) {
                chosen = this._lods[idx];
                if (idx === 0 && !this._lod0Inited) {
                    this._lod0Inited = true;
                    this.onLod0Initiated();
                }
                break;
            }
        }
        this._lods.forEach(lod => lod.active = lod === chosen);
    }

    protected updateScale(magnified?: num.pct): void {
        this.node.setScale(Vec3.multiplyScalar(v3a, this._real, magnified ?? solar.getMagnified(this.cat)));
    }

    protected _lod0!: Node;
    protected _lod1?: Node;
    protected _lod2?: Node;
    protected _lod3?: Node;
    protected _viewer: Camera;
    protected _lods: Node[];
    protected _lod0Inited: boolean = false;
    protected _real: Vec3 = new Vec3(1, 1, 1);
}

const v3a = new Vec3;